﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Serialization.Formatters.Binary;
using messageData;
using minichatclient.Utils;
using GalaSoft.MvvmLight.Messaging;

namespace minichatclient
{
    public class Client
    {
        private Socket clientSocket;
        private string ip = "192.168.50.9";
        private int port = 9999;
        private byte[] data;
        private int bufferSize = 1024 * 64;
        public User user;
        private Dictionary<string, byte[]> md5ToFile = new Dictionary<string, byte[]>();
        private Dictionary<string, string> fileNameToMD5 = new Dictionary<string, string>();
        private Dictionary<string, string> md5ToFileName = new Dictionary<string, string>();
        public FileHelper file_helper;
        Thread t;

        //public Client()
        //{
        //    this.user = new User();
        //    //user.uid = "u1";
        //    clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //    data = new byte[bufferSize];
        //    file_helper = new FileHelper();
        //    file_helper.path = "D:\\";
        //    login_success = false;
        //    login_failed = false;
        //}
        public Client()
        {
            this.user = new User();
            //user.uid = "u1";
            clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            data = new byte[bufferSize];
            file_helper = new FileHelper();
        }

        public bool Start()
        {
            try
            {
                IPAddress ipaddress = IPAddress.Parse(ip);
                EndPoint point = new IPEndPoint(ipaddress, port);
                clientSocket.Connect(point);
                t = new Thread(ReceiveMessage);
                t.IsBackground = true;
                t.Start();
                return true;
            }
            catch
            {
                return false;
            }
        }

        public void sendMd5OrFile(string file_name, string receiver, bool is_image)
        {
            byte[] file_data = file_helper.ReadFileToByte("");
            string md5 = Md5Helper.GetMd5(file_data);
            //file_data md5存数据库
            md5ToFileName[md5] = file_name;
            //md5ToFile[md5] = file_data;
            sendMD5(md5, file_name, receiver, is_image);
        }

        public void sendMD5(string md5, string file_name, string receiver, bool is_image)
        {

            //data: [0]md5 [1]file_name
            CommunicateMessage message = new CommunicateMessage();
            message.dataType = messageType.md5;
            message.Data.Add(md5);
            message.Data.Add(file_name);
            message.Data.Add(is_image);
            message.date = DateTime.Now;
            message.receiver = receiver;
            message.sender = user.uid;
            sendMessage(message);
        }

        public void sendSentence(string sentence, string receiver)
        {
            CommunicateMessage message = new CommunicateMessage();
            message.dataType = messageType.sentence;
            message.sender = user.uid;
            message.receiver = receiver;
            message.date = DateTime.Now;
            message.Data.Add(sentence);
            sendMessage(message);
        }

        //private void send


        public bool sendFile(string md5, string file_name, string receiver, bool is_image)
        {

            //data [0]file_byte [1]file_name [2]md5
            string temp = this.file_helper.path;
            this.file_helper.path = "";
            byte[] file_byte = file_helper.ReadFileToByte(file_name);
            this.file_helper.path = temp;
            Console.WriteLine("传送文件大小为:" + file_byte.Length);
            CommunicateMessage file_message = new CommunicateMessage();
            file_message.Data.Add(file_byte);
            file_message.Data.Add(file_name);
            file_message.Data.Add(md5);
            file_message.receiver = receiver;
            file_message.sender = user.uid;
            file_message.date = DateTime.Now;
            if(is_image) file_message.dataType = messageType.image;
            else file_message.dataType = messageType.file;
            //file_message.dataType = m_type;
            sendMessage(file_message);
            Console.WriteLine("传送完成");
            return true;
        }
        //public void ReceiveMessage()
        //{
        //    //这两个变量在关于长度的4字节被分开在两个缓冲数组才使用

        //    int temp_len = -1;
        //    int temp_val = 0;

        //    //读取缓冲数组长度
        //    int length = 0;

        //    //消息需要的缓冲数组的个数
        //    int count = 0;
        //    //偏移量
        //    int offset = 0;
        //    int new_offset = 0;
        //    //已读长度
        //    int has_read_len = 0;
        //    byte[] my_data = new byte[0];//自定义缓冲区
        //    List<byte> has_read_data = new List<byte>();
        //    string message = String.Empty;
        //    bool flag = false;
        //    while (true)
        //    {
        //        //count = 0;
        //        try
        //        {
        //            length = clientSocket.Receive(data);
        //            offset = 0;
        //            Console.WriteLine(length);
        //        }

        //        catch
        //        {
        //            //Console.Writ)
        //            return;
        //        }

        //        //    //if (length == bufferSize) continue;
        //        //    //断开连接
        //        if (length == 0)
        //        {

        //            Console.WriteLine("与服务器断开连接");
        //            return;
        //        }
        //        if (flag && has_read_len < my_data.Length)
        //        {
        //            Console.WriteLine("包分裂");
        //            for (int i = has_read_len; i < has_read_len + length; i++)
        //            {
        //                my_data[i] = data[i - has_read_len];
        //            }
        //            if (has_read_len == my_data.Length)
        //            {
        //                sendMessage(my_data);
        //                flag = false;
        //            }
        //            else
        //            {
        //                continue;
        //            }
        //        }

        //        Console.WriteLine(data[696] + "sssss");
        //    st: if (count == 0)//解析包头
        //        {
        //            //header结构: | 1 |  4  |   第一个字节判断是否是一个新的包(必须全部为255),后四字节是长度
        //            uint val = 0;
        //            has_read_len = 0;

        //            //关于长度的4字节被分开在了两个缓冲数组里
        //            if (temp_len >= 0)
        //            {
        //                val = (uint)temp_val;
        //                for (int i = 0; i < 4 - temp_len; i++)
        //                {
        //                    val = val << 8;
        //                    val += data[i];
        //                }
        //                temp_len = -1;
        //                temp_val = 0;
        //            }
        //            else
        //            {
        //                if (data[offset++] != 113)
        //                {
        //                    //后续改为抛异常
        //                    Console.WriteLine("包结构错误");
        //                    continue;
        //                }
        //                //关于长度的4字节被分开在了两个缓冲数组里
        //                if (offset + 4 > bufferSize)
        //                {

        //                    temp_len = bufferSize - offset;
        //                    for (int i = offset; i < bufferSize; i++)
        //                    {
        //                        temp_val = temp_val << 8;
        //                        temp_val += data[i];
        //                    }
        //                    continue;
        //                }

        //                //正常情况
        //                for (int i = offset; i < 4 + offset; i++)
        //                {
        //                    val = val << 8;
        //                    val += data[i];
        //                }
        //            }

        //            offset = (offset + 4) % bufferSize;
        //            count = (int)((val + offset + bufferSize - 1) / (uint)bufferSize);//向上取整
        //            new_offset = (int)((val + offset) % bufferSize);
        //            if (new_offset == 0) new_offset = bufferSize;
        //            my_data = new byte[val];
        //            for (int i = offset; i < Math.Min(bufferSize, val + offset); i++)
        //            {
        //                my_data[has_read_len++] = data[i];
        //            }
        //            count--;
        //            if (count == 0 && (offset + val == bufferSize || data[offset + val] != 113))//无粘包,复原
        //            {

        //                Console.WriteLine("不粘锅");
        //                Console.WriteLine(has_read_len + " " + my_data.Length + "!!!...");
        //                if (has_read_len == my_data.Length) handle_data(my_data);
        //                else
        //                {
        //                    Console.WriteLine("包分裂！！！！");
        //                    flag = true;
        //                }
        //                //Console.Write("起始地址:" + offset + " ");
        //                //handle_data(my_data);
        //                offset = 0;
        //                new_offset = 0;
        //                has_read_len = 0;
        //            }
        //            else if (count == 0)
        //            {
        //                if (data[offset + val] == 113)//粘包
        //                {
        //                    Console.WriteLine("粘包yyy");
        //                    Console.Write("起始地址:" + offset + " ");
        //                    handle_data(my_data);
        //                    offset = new_offset;
        //                    new_offset = 0;
        //                    has_read_len = 0;
        //                    goto st;
        //                }
        //                else
        //                {
        //                    //not happen 后续改为抛异常
        //                    Console.WriteLine("传递包出错");
        //                }
        //            }

        //        }
        //        else if (count == 1)
        //        {
        //            for (int i = 0; i < new_offset; i++)
        //            {
        //                my_data[has_read_len++] = data[i];
        //            }
        //            count--;
        //            //Console.WriteLine(data[new_offset]);
        //            if (new_offset == bufferSize || data[new_offset] != 113)//无粘包,复原
        //            {
        //                Console.WriteLine("不粘锅111");
        //                //Console.Write(offset+":  ");

        //                Console.WriteLine(has_read_len + " " + my_data.Length + "!!!...");
        //                if (has_read_len == my_data.Length) handle_data(my_data);
        //                else
        //                {
        //                    Console.WriteLine("包分裂！！！！");
        //                    flag = true;
        //                }
        //                offset = 0;
        //                new_offset = 0;
        //                has_read_len = 0;
        //            }
        //            else if (data[new_offset] == 113)//粘包
        //            {
        //                Console.WriteLine("粘包111");
        //                //Console.Write(offset + ":  ");
        //                handle_data(my_data);
        //                offset = new_offset;
        //                new_offset = 0;
        //                goto st;
        //            }

        //        }
        //        else
        //        {
        //            for (int i = 0; i < bufferSize; i++)
        //            {
        //                my_data[has_read_len++] = data[i];
        //            }
        //            count--;
        //        }
        //    }

        //}

        public void ReceiveMessage()
        {
            //这两个变量在关于长度的4字节被分开在两个缓冲数组才使用
            int temp_len = -1;
            int temp_val = 0;

            //读取缓冲数组长度
            int length = 0;

            //消息需要的缓冲数组的个数
            int count = 0;
            //偏移量
            int offset = 0;
            int new_offset = 0;
            //已读长度
            int has_read_len = 0;
            byte[] my_data = new byte[0];//自定义缓冲区
            List<byte> has_read_data = new List<byte>();
            string message = String.Empty;
            bool flag = true;
            bool link = false;
            int val = 0;
            while (true)
            {
                //count = 0;
                try
                {
                    length = clientSocket.Receive(data);
                    offset = 0;
                }

                catch
                {
                    //Console.Writ)
                    return;
                }



                if (length == 0)
                {

                    Console.WriteLine("与服务器断开连接");
                    return;
                }

                if (flag)
                {
                    if (data[offset++] != 113)
                    {
                        Console.WriteLine("失败");
                    }
                    for (int i = offset; i < 4 + offset; i++)
                    {
                        val = val << 8;
                        val += data[i];
                    }
                    offset = (offset + 4) % bufferSize;
                    has_read_len = 0;
                    my_data = new byte[val];
                    flag = false;
                }
                int temp = my_data.Length - has_read_len + offset;
                for (int i = offset; i < Math.Min(length, temp); i++)
                {
                    my_data[has_read_len++] = data[i];
                }
            lp: if (has_read_len == my_data.Length)
                {
                    handle_data(my_data);
                    if (length < bufferSize && temp < length && data[temp] == 113)//粘包
                    {
                        Console.WriteLine("粘包");
                        offset = temp + 1;
                        for (int i = offset; i < 4 + offset; i++)
                        {
                            val = val << 8;
                            val += data[i];
                        }
                        Console.WriteLine(val);
                        offset = (offset + 4) % bufferSize;
                        has_read_len = 0;
                        my_data = new byte[val];
                        temp = Math.Min(length, val + offset);
                        for (int i = offset; i < temp; i++)
                        {
                            my_data[has_read_len++] = data[i];
                        }
                        goto lp;

                    }
                    else
                    {
                        flag = true;
                    }
                }



            }

        }

        private void handle_data(byte[] my_data)
        {
            Message obj;
            try
            {
                obj = (Message)tools.Un_Serializable_obj(my_data);
            }
            catch
            {
                Console.WriteLine("解析出错");
                return;
            };


            if (obj.message_number == 1)
            {
                SignalMessage signal_message = (SignalMessage)obj;
                if (signal_message.signalType == signalType.login_failed)
                {
                    Messenger.Default.Send(string.Empty, "login");
                }
                else if (signal_message.signalType == signalType.login_success)
                {
                    user.uid = (string)signal_message.parameter[0];
                    Messenger.Default.Send(user.uid, "login");
                }
                else if (signal_message.signalType == signalType.SelectUserById)
                {
                    User user = (User)signal_message.parameter[0];
                    Messenger.Default.Send(user, "userDataInit");
                    Messenger.Default.Send(user, "findFriendById");
                }
                else if (signal_message.signalType == signalType.SelectUserLikeName)
                {
                    List<User> users = (List<User>)signal_message.parameter[0];
                    Messenger.Default.Send(users, "findFriendLikeName");
                }
                else if (signal_message.signalType == signalType.SelectGroupById)
                {
                    Group group = (Group)signal_message.parameter[0];
                    Messenger.Default.Send(group, "findGroupById");
                }
                else if (signal_message.signalType == signalType.SelectGroupLikeName)
                {
                    List<Group> groups = (List<Group>)signal_message.parameter[0];
                    Messenger.Default.Send(groups, "findGroupLikeName");
                }
                else if (signal_message.signalType == signalType.SelectAddedFriends)
                {
                    List<User> users = (List<User>)signal_message.parameter[0];
                    Messenger.Default.Send(users, "friendData");
                }
                else if (signal_message.signalType == signalType.SelectAddedGroups)
                {
                    List<Group> groups = (List<Group>)signal_message.parameter[0];
                    Messenger.Default.Send(groups, "groupData");
                }
                else if (signal_message.signalType == signalType.SelectGroupUsers)
                {
                    List<User> users = (List<User>)signal_message.parameter[0];
                    foreach (var user in users)
                    {
                        user.toString();
                    }
                }
                else if (signal_message.signalType == signalType.SelectAllMessages)
                {
                    List<CommunicateMessage> messages = (List<CommunicateMessage>)signal_message.parameter[0];
                    Messenger.Default.Send(messages, "allMessagesBetweenTwoUser");
                }
                else if (signal_message.signalType == signalType.SelectGroupMessage)
                {
                    List<CommunicateMessage> messages = (List<CommunicateMessage>)signal_message.parameter[0];
                    Messenger.Default.Send(messages, "allMessagesInGourp");
                }
                else if (signal_message.signalType == signalType.not_found_md5)
                {
                    string md5 = (string)signal_message.parameter[0];
                    string file_name = (string)signal_message.parameter[1];
                    bool is_image = (bool)signal_message.parameter[2];
                    Console.WriteLine("no md5");
                    bool v = sendFile(md5, file_name, signal_message.sender, is_image);
                    //this.md5ToFile.Remove(md5);
                    Console.WriteLine("文件传输成功111");
                }
                else if (signal_message.signalType == signalType.add_success)
                {
                    string md5 = (string)signal_message.parameter[0];
                    string file_name = (string)signal_message.parameter[1];
                    //this.md5ToFile.Remove(md5);
                    Console.WriteLine("文件传输成功222");
                }
                else if (signal_message.signalType == signalType.register_success)
                {
                    string user_id = (string)signal_message.parameter[0];
                    Messenger.Default.Send(user_id, "register");
                }
                else if (signal_message.signalType == signalType.register_failed)
                {
                    Messenger.Default.Send(string.Empty, "register");
                }
            }
            else if (obj.message_number == 2)
            {
                CommunicateMessage communication_message = (CommunicateMessage)obj;
                if (communication_message.dataType == messageType.sentence)
                {
                    Messenger.Default.Send(communication_message, "textMessage");
                    Console.WriteLine((string)communication_message.Data[0]);
                }
                else if (communication_message.dataType == messageType.image || communication_message.dataType == messageType.file)
                {
                    string file_name = (string)communication_message.Data[1];
                    string md5 = (string)communication_message.Data[2];
                    this.md5ToFileName[md5] = file_name;
                    string suffix = ((string)communication_message.Data[1]).Substring(((string)communication_message.Data[1]).LastIndexOf("."));
                    if (communication_message.dataType == messageType.image)
                    {
                        file_helper.writeByteToFile((byte[])communication_message.Data[0], @$"\Images\{((string)communication_message.Data[1]).Substring(((string)communication_message.Data[1]).LastIndexOf('\\') + 1)}");
                        communication_message.Data[1] = ((string)communication_message.Data[1]).Substring(((string)communication_message.Data[1]).LastIndexOf('\\') + 1);
                        if (suffix == ".gif")
                        {
                            Messenger.Default.Send(communication_message, "gifMessage");
                        }
                        else
                        {
                            Messenger.Default.Send(communication_message, "imageMessage");
                        }
                    }
                    else
                    {
                        file_helper.writeByteToFile((byte[])communication_message.Data[0], @$"\Files\{((string)communication_message.Data[1]).Substring(((string)communication_message.Data[1]).LastIndexOf('\\') + 1)}");
                        communication_message.Data[1] = ((string)communication_message.Data[1]).Substring(((string)communication_message.Data[1]).LastIndexOf('\\') + 1);
                        Messenger.Default.Send(communication_message, "fileMessage");
                    }
                }
                // md5包 [0]md5, [1]file_name
                else if (communication_message.dataType == messageType.md5)
                {
                    if (md5ToFileName.ContainsKey((string)communication_message.Data[0]))
                    {
                        SignalMessage signalmessage = new SignalMessage();
                        signalmessage.sender = this.user.uid;
                        signalmessage.receiver = communication_message.sender;
                        signalmessage.signalType = signalType.add_success;
                        signalmessage.parameter.Add(communication_message.Data[0]);
                        signalmessage.parameter.Add(communication_message.Data[1]);
                        signalmessage.parameter.Add(communication_message.Data[2]);
                        sendMessage(signalmessage);
                        Console.WriteLine("收到文件且本地存在,md5为" + (string)communication_message.Data[0] +
                            "文件名为:" + (string)communication_message.Data[1]);
                    }
                    else
                    {
                        SignalMessage signal_message = new SignalMessage();
                        signal_message.sender = this.user.uid;
                        signal_message.receiver = communication_message.sender;
                        signal_message.signalType = signalType.not_found_md5;
                        signal_message.parameter.Add(communication_message.Data[0]);
                        signal_message.parameter.Add(communication_message.Data[1]);
                        signal_message.parameter.Add(communication_message.Data[2]);
                        sendMessage(signal_message);
                    }
                }
            }

        }
        public void sendMessage(Message m)
        {
            var data = tools.Serializable_obj(m);
            // Console.WriteLine("序列化后大小为:" + data.Length);
            sendMessage(data);
        }
        public void sendMessage(byte[] data)
        {
            //防止byte数组太大,我们后续需要在这添加异常处理
            byte[] data2 = new byte[5 + data.Length];
            data2[0] = 113;
            int len = data.Length;
            for (int i = 4; i >= 1; i--)
            {
                int a = len >> 8;
                data2[i] = (byte)(len - (a << 8));
                len = a;
            }
            len = data.Length;
            for (int i = 5; i < 5 + len; i++)
            {
                data2[i] = data[i - 5];
            }

            clientSocket.Send(data2);
        }

        //public void sendMessage(string message)
        //{
        //    byte[] data = Encoding.UTF8.GetBytes(message);
        //    byte[] data2 = new byte[5 + data.Length];
        //    data2[0] = 255;
        //    int len = data.Length;
        //    for (int i = 4; i >= 1; i--)
        //    {
        //        int a = len >> 8;
        //        data2[i] = (byte)(len - (a << 8));
        //        len = a;
        //    }
        //    len = data.Length;
        //    for (int i = 5; i < 5 + len; i++)
        //    {
        //        data2[i] = data[i - 5];
        //    }

        //    clientSocket.Send(data2);
        //}


        public void login(string name, string pwd)
        {
            QueryDbMessage message = new QueryDbMessage();
            message.method_type = methodType.Login;
            message.parameter.Add(name);
            message.parameter.Add(pwd);
            message.sender = name;
            sendMessage(message);
        }

        public void logout()
        {
            QueryDbMessage message = new QueryDbMessage();
            message.method_type = methodType.Logout;
            message.parameter.Add(user.uid);
            message.sender = user.uid;
            sendMessage(message);
        }

        public void register(User user)
        {
            QueryDbMessage message = new QueryDbMessage();
            message.method_type = methodType.Register;
            message.parameter.Add(user);
            //message.sender = user.uid;
            sendMessage(message);
        }

        public void addFriend(string friendName)
        {
            QueryDbMessage message = new QueryDbMessage();
            message.sender = user.uid;

            message.parameter.Add(user.uid);
            message.parameter.Add(friendName);
            message.method_type = methodType.AddFriend;
            sendMessage(message);
        }

        public void addGroup(string groupName)
        {
            QueryDbMessage message = new QueryDbMessage();
            message.sender = user.uid;
            message.parameter.Add(user.uid);
            message.parameter.Add(groupName);
            message.method_type = methodType.AddGroup;
            sendMessage(message);
        }

        public void deleteFriend(string friendName)
        {
            QueryDbMessage message = new QueryDbMessage();
            message.sender = user.uid;
            message.parameter.Add(user.uid);
            message.parameter.Add(friendName);
            message.method_type = methodType.DeleteFriend;
            sendMessage(message);
        }

        public void withDrawGroup(string groupName)
        {
            QueryDbMessage message = new QueryDbMessage();
            message.sender = user.uid;
            message.method_type = methodType.WithdrawGroup;
            message.parameter.Add(user.uid);
            message.parameter.Add(groupName);
            sendMessage(message);
        }

        public void createGroup(string groupName)
        {
            QueryDbMessage message = new QueryDbMessage();
            message.sender = user.uid;
            message.method_type = methodType.CreateGroup;
            message.parameter.Add(user.uid);
            Group group = new Group();
            group.gname = groupName;
            message.parameter.Add(group);
            sendMessage(message);
        }

        public void selectUserById(string userName)
        {
            QueryDbMessage message = new QueryDbMessage();
            message.sender = user.uid;
            message.method_type = methodType.SelectUserById;
            message.parameter.Add(userName);
            sendMessage(message);
        }

        public void selectUserLikeName(string name)
        {
            QueryDbMessage message = new QueryDbMessage();
            message.sender = user.uid;
            message.method_type = methodType.SelectUserLikeName;
            message.parameter.Add(name);
            sendMessage(message);
        }

        public void selectGroupById(string groupName)
        {
            QueryDbMessage message = new QueryDbMessage();
            message.sender = user.uid;
            message.method_type = methodType.SelectGroupById;
            message.parameter.Add(groupName);
            sendMessage(message);
        }

        public void selectGroupLikeName(string groupName)
        {
            QueryDbMessage message = new QueryDbMessage();
            message.sender = user.uid;
            message.method_type = methodType.SelectGroupLikeName;
            message.parameter.Add(groupName);
            sendMessage(message);
        }

        public void selectAddedFriends()
        {
            QueryDbMessage message = new QueryDbMessage();
            message.sender = user.uid;
            message.parameter.Add(user.uid);
            message.method_type = methodType.SelectAddedFriends;
            sendMessage(message);
        }

        public void selectAddedGroup()
        {
            QueryDbMessage message = new QueryDbMessage();
            message.sender = user.uid;
            message.method_type = methodType.SelectAddedGroups;
            message.parameter.Add(user.uid);
            sendMessage(message);
        }

        public void selectGroupUsers(string groupName)
        {
            QueryDbMessage message = new QueryDbMessage();
            message.sender = user.uid;
            message.method_type = methodType.SelectGroupUsers;
            message.parameter.Add(groupName);
            sendMessage(message);
        }

        public void selectAllMessages(string friendName)
        {
            QueryDbMessage message = new QueryDbMessage();
            message.sender = user.uid;
            message.parameter.Add(user.uid);
            message.parameter.Add(friendName);
            message.method_type = methodType.SelectAllMessages;
            sendMessage(message);
        }

        public void selectGroupMessages(string groupName)
        {
            QueryDbMessage message = new QueryDbMessage();
            message.sender = user.uid;
            message.method_type = methodType.SelectGroupMessage;
            message.parameter.Add(groupName);
            sendMessage(message);
        }

        public void sendMd5OrFile(string file_name, string receiver, string fileFullPath, bool is_image = false)
        {
            string temp = this.file_helper.path;
            file_helper.path = fileFullPath;
            sendMd5OrFile(fileFullPath, receiver, is_image);
            this.file_helper.path = temp;
        }
    }
}
